program chamtudong;

{$mode objfpc}{$H+}

uses
  Classes,
  SysUtils,
  DateUtils,
  fileutil,
  process;

const
  dauthumuc: char = '\';
var
  sfn: string;
  tgdich: integer;  //thoi gian toi da de dich chuong trinh
  tgchay: integer; //thoi gian toi da de chay 1 test
  kieu: byte; //kieu bai
  in_name, out_name: string;
  bonho: integer; //bo nho toi da
  trangthai: byte;
  input: TStringList;
  dung: byte;
  i:byte;
  xem : Boolean;
  ghi:longint;
procedure showUsage;
begin
  writeln('Cham thi tu dong - by Tran Huu Nam (huunam0@gmail.com)');
  WriteLn('Usage: chamtudong.exe <full_path_of_source_file>');
end;

procedure batdau;
begin
  tgdich := 20000;
  tgchay := 5000;
  bonho := 1024;
  kieu := 1;
  in_name := '';
  out_name := '';
  trangthai := 0;
  ghi:=0;
end;
procedure DatTrangthai(tt: byte);
begin
  trangthai := tt;
end;
procedure chuanhoaxau(var s : string);
begin
  s:=trim(s);
  while pos('  ',s)>0 do delete(s,pos('  ',s),1);
end;

procedure ghink(bai,duoi,noidung: string);
var
  f:TextFile;
  fn : string;
begin
  fn:=ChangeFileExt(bai,duoi);
  AssignFile(f,fn);
  if ghi=0 then
    rewrite(f)
  else
    if FileExists(fn) then
      Append(f)
    else
      Rewrite(f);
  writeln(f,noidung);
  close(f);
  inc(ghi);
end;
procedure ghink(bai,duoi: string; noidung:integer);
var
  f:TextFile;
  fn : string;
begin
  fn:=ChangeFileExt(bai,duoi);
  AssignFile(f,fn);
  if ghi=0 then
    rewrite(f)
  else
    if FileExists(fn) then
      Append(f)
    else
      Rewrite(f);
  writeln(f,noidung);
  close(f);
  inc(ghi);
end;

procedure dich(bai: string);
var
  Ps: TProcess;
  Lucbd: TDateTime;
  kq: TStringList;
  thoigian: double;
  srec:TSearchRec;
  thumuc:string;
begin
  writeln('BIEN DICH CHUONG TRINH');
  DeleteFile(ChangeFileExt(bai,'.o'));
  DeleteFile(ChangeFileExt(bai,'.exe'));
  thumuc:=ExtractFilePath(bai);
  if FindFirst(ChangeFileExt(bai,'_*.txt'), faAnyFile, srec) = 0 then
    repeat
      DeleteFile(thumuc+srec.Name);
    until FindNext(srec) <> 0;
  Ps := TProcess.Create(nil);
  ps.CommandLine := 'fpc ' + bai;
  ps.Options := [poUsePipes];
  Lucbd := now;
  ps.Execute;
  DatTrangthai(1);
  while ps.Running and (MilliSecondSpan(Lucbd, now) <= tgdich) do
    Sleep(10);
  if ps.Running then
  begin
    ps.Terminate(-1);
    DatTrangthai(11);
    ghink(bai,'_dich.txt','bien dich qua lau -> thu cham lai');
    writeln('Bien dich qua lau');
  end
  else
  begin
    thoigian := MilliSecondSpan(Lucbd, now);
    kq := TStringList.Create;
    kq.LoadFromStream(ps.Output);
    if ps.ExitStatus <> 0 then
    begin
      DatTrangthai(12);
      kq.SaveToFile(ChangeFileExt(bai,'_dich.txt'));
      writeln('Loi bien dich -', thoigian: 0: 0, 'ms');
    end
    else
    begin
      if FileExists(ChangeFileExt(bai,'.exe')) then
        DatTrangthai(14)
      else
        DatTrangthai(13);
      DeleteFile(ChangeFileExt(bai,'.o'));
      writeln('Bien dich thanh cong trong ', thoigian: 0: 0, 'ms');
    end;
  end;
end;

procedure doccauhinh(thumuc: string);
var
  f: TextFile;
  fn: string;
begin

  fn := thumuc + '\chamthi.inf';
  if not FileExists(fn) then
  begin
    //ghi loi khong tim thay file cau hinh
    DatTrangthai(21);
    Exit;
  end;
  AssignFile(f, fn);
  Reset(f);
  ReadLn(f, fn);
  writeln(fn);
  if not EOF(f) then
    readln(f, kieu);
  if not EOF(f) then
    readln(f, tgchay);
  if not EOF(f) then
    readln(f, bonho);
  if not EOF(f) then
    readln(f, in_name);
  if not EOF(f) then
    readln(f, out_name);
  if out_name='' then
    out_name:=ExtractFileNameOnly(thumuc)+'.out';
  CloseFile(f);
  //DatTrangthai(6);
end;

(*
 *Đọc file input vào danh sách
 *Input:
 +thucmuc: thư mục chính, chứa bài toán
 +tenbai: tên của bài toán
 *Output: lưu vào biến toàn cục "input"
 *)
procedure docdsdulieuvao(thumuc: string);
var
  srec: TSearchRec;
begin
  input := TStringList.Create;
  if FindFirst(thumuc + dauthumuc + 'test*.inp', faAnyFile, srec) = 0 then
    repeat
      input.Append(thumuc+dauthumuc+srec.Name);
    until FindNext(srec) <> 0;
end;

(* So sánh kết quả output chạy với output mẫu, kiểu 1 - so khớp
** Input: tên của 2 file output
** Return: 1 nếu đúng, 0 nếu sai
*)
function soketqua1(outmau,outchay:string):byte;
var
  //txt : TStringList;
  s1, s2 : string;
  f1, f2 : TextFile;
  dung : Boolean;
begin
  dung:=true;
  AssignFile(f1,outmau);
  AssignFile(f2,outchay);
  reset(f1);
  Reset(f2);
  while not EOF(f1) do begin
    readln(f1, s1);
    chuanhoaxau(s1);
    if Length(s1)>0 then
      begin
        if EOF(f2) then
          begin
            dung:=false;
            break;
          end
        else
          begin
            ReadLn(f2, s2);
            chuanhoaxau(s2);
            if pos('|'+s2+'|','|'+s1+'|')=0 then
              begin
                dung:=false;
                break;
              end;
          end;
      end;
  end;
  CloseFile(F1);
  CloseFile(f2);
  if dung then
    exit(1)
  else
    exit(0);
end;
(* So sánh kết quả output chạy với output mẫu, kiểu 2
** Input: tên của 2 file output
** Return: 1 nếu đúng, 0 nếu sai
*)
function soketqua2(outmau,outchay:string):byte;
begin
  exit(0);
end;

(* So sánh kết quả output chạy với output mẫu, kiểu 4 - thu tu tuy y
** Input: tên của 2 file output
** Return: 1 nếu đúng, 0 nếu sai
*)
function soketqua4(outmau,outchay:string):byte;
var
  txt : TStringList;
  s1, s2 : string;
  f2 : TextFile;
  dung : Boolean;
  vitri : integer;
begin
  dung:=true;
  AssignFile(f2,outchay);
  Reset(f2);
  txt:=TStringList.Create;
  txt.LoadFromFile(outmau);
  while not EOF(f2) do begin
    readln(f2, s2);
    chuanhoaxau(s2);
    if txt.Find(s2,vitri) then
        txt.Delete(vitri)
    else
        break;
  end;
  close(f2);
  if txt.Count>0 then
    if length(txt.Strings[1])>0 then exit(0);
  exit(1);
end;

(* So sánh kết quả output chạy với output mẫu, kiểu 3 - thu tu tuy y
** Input: tên của 2 file output
** Return: 1 nếu đúng, 0 nếu sai
*)
function soketqua3(outmau,outchay:string):byte;
var
  txt : TStringList;
  s1, s2 : string;
  f2,f1 : TextFile;
  dung : Boolean;
  vitri : integer;
  sl,i,j,sd:word;
  a : array[1..1000] of byte;
begin
  dung:=true;
  AssignFile(f2,outmau);
  Reset(f2);
  txt:=TStringList.Create;
  //txt.LoadFromFile(outchay);
  (**)
  AssignFile(f1,outchay);
  reset(f1);
  while not eof(f1) do begin
    readln(f1,s1);
    chuanhoaxau(s1);
    if length(s1)>0 then txt.Add(s1);
  end;
  close(f1);
  (**)
  sl:=-1;
  if not eof(f2) then readln(f2,sl);
  if IntToStr(sl) <> trim(txt.Strings[0]) then
    exit(0);

  txt.Delete(0);
  if sl<=0 then exit(1);
  while txt.Count>sl do txt.Delete(sl);

  i:=1;
  fillchar(a,SizeOf(a),0);
  sd:=0;
  while not EOF(f2) do begin
    readln(f2, s2);
    chuanhoaxau(s2);
    if txt.Find(s2,vitri) then
        a[i]:=1;
    //else
        //break;
    inc(i);
    if i>sl then
      begin
        sd:=0;
        for j:= 1 to sl do
          inc(sd,a[j]);
        if sd=sl then break;
        i:=1;
        fillchar(a, sizeof(a), 0);
      end;

  end;
  close(f2);
  if sd=sl then
    exit(1)
  else
    exit(0);
end;

(* So sánh kết quả output chạy với output mẫu
** Input: tên của 2 file output, kieu: kiểu so sánh
** Return: 1 nếu đúng, 0 nếu sai
*)
function soketqua(outmau, outchay: string; kieu: byte): byte;
begin
  case kieu of
  1: exit(soketqua1(outmau,outchay));
  2: exit(soketqua2(outmau,outchay));
  3: exit(soketqua3(outmau,outchay));
  4: exit(soketqua4(outmau,outchay));
  else
    exit (0);
  end;
end;

function chaythu(exefile: string; inp: string): byte;
var
  tmchay: string;
  //txt : TStringList;
  ct: TProcess;
  Lucbd: TDateTime;
  thoigian: double;
  bRet: boolean;
  dulieu: string;
begin
  tmchay := ExtractFilePath(exefile);
  dulieu := tmchay + in_name;

  //if FileExists(dulieu) then
  DeleteFile(dulieu); //xoa input cu
  DeleteFile(tmchay+out_name);   //xoa output cu
  DeleteFile(ChangeFileExt(exefile,'_chay.txt'));

  if in_name<>'' then
    begin
      bret := CopyFile(inp, dulieu); //chep input moi
      if not bRet then  //Khong chep duoc tep du lieu vao
      begin
        DatTrangthai(22);
        exit(0);
      end;
    end;
  ct := TProcess.Create(nil);
  ct.CommandLine := exefile;
  ct.CurrentDirectory:=ExtractFileDir(exefile);
  Lucbd := now;
  ct.Execute;
  //if in_name='' then
    //file2stream(inp,ct.Input);
  while ct.Running and (MilliSecondSpan(Lucbd, now) <= tgchay) do
    Sleep(10);
  if ct.Running then
  begin
    ct.Terminate(-1);
    DatTrangthai(32);
    ghink(exefile,'_chay.txt','chuong trinh chay qua lau');
    exit(0);
  end
  else
  begin
    thoigian := MilliSecondSpan(Lucbd, now);
    if ct.ExitStatus <> 0 then
    begin
      DatTrangthai(31);
      //kq.SaveToFile('_chay.txt');
      //writeln('Loi thuc thi -', thoigian: 0: 0, 'ms');
      ghink(exefile,'_chay.txt','Loi thuc thi (runtime error)');
      exit(0);
    end
    else
    begin
      //DatTrangthai(9);
      //writeln('Chay thanh cong trong ', thoigian: 0: 0, 'ms');
      //txt.LoadFromFile(tmchay+out_name);
      //so sanh voi output mau
      dulieu := tmchay + out_name;
      //if not FileExists(dulieu) then
        //stream2file(ct.Output,dulieu);
      if not FileExists(dulieu) then   //khong co output
      begin
        DatTrangthai(33);
        exit(0);
      end
      else            //co file output
      begin
        DatTrangthai(34);
        exit(soketqua(ChangeFileExt(inp, '.out'), dulieu,kieu));
      end;
    end;
  end;

end;

procedure chay(exename: string);
var
  root: string;
  ref: string;
  nhatki: string;
  i: byte;
  thumuc,tenbai:string;
begin
  writeln('CHAY CHUONG TRINH');
  tenbai:=ExtractFileNameOnly(exename);
  i:=Pos('_',tenbai);
  if i>1 then
    tenbai:=copy(tenbai,1,i-1);
  thumuc:=ExtractFileDir(exename);
  root := ExtractFileDir(thumuc); //remove <SBD> dir
  root := ExtractFileDir(root); //remove upload dir
  ref := root +  '\baitoan\' + tenbai;
  //writeln('ref=',ref);
  //exit;
  doccauhinh(ref);//kieu+ghbonho+ghthoigian
  docdsdulieuvao(ref);
  ghink(exename,'_kq.txt',input.Count); //so luong text
  writeln('So luong bo test: ',input.Count);
  dung := 0;
  for i := 1 to input.Count do
    if chaythu(exename, input.Strings[i-1])>0 then
      begin
        inc(dung);
        if xem then
           writeln(ExtractFileNameOnly(input.Strings[i-1])+': DUNG');
        ghink(exename,'_kq.txt',ExtractFileNameOnly(input.Strings[i-1])+': DUNG');
      end
    else
      begin
        if xem then
           writeln(ExtractFileNameOnly(input.Strings[i-1])+': SAI');
        ghink(exename,'_kq.txt',ExtractFileNameOnly(input.Strings[i-1])+': SAI');
        DeleteFile(ChangeFileExt(exename,'_'+ExtractFileNameOnly(input.Strings[i-1])+'.txt'));
        renamefile(ExtractFilePath(exename)+out_name,ChangeFileExt(exename,'_'+ExtractFileNameOnly(input.Strings[i-1])+'.txt'));
      end;
  writeln('So test dung: ', dung);
  input.Free;
  ghink(exename,'_kq.txt',dung); //so luong test dung
  DeleteFile(exename);
  DeleteFile(ExtractFilePath(exename)+in_name);
  DeleteFile(ExtractFilePath(exename)+out_name);
end;

procedure chambai(bai: string);   //bai=bai lam abc_2.pas
begin
  if xem then writeln('Bat dau cham bai "', bai, '"');

  dich(bai);
  if trangthai=14 then
    chay(ChangeFileExt(bai,'.exe'));
end;


procedure test_cham1;
begin
  //sfn:= 'C:\wamp\www\chamthitudong\upload\nvquang\modtke.pas';
  sfn:= 'C:\wamp\www\chamtudong\upload\thnam\ruttien.pas';
  chambai(sfn);
  readln;
  halt;
end;

{$R *.res}

begin
  batdau;
  //test_cham1;
  if Paramcount = 0 then
    showUsage
  else
  begin
    xem:=pos('_',ParamStr(0))>0;
    for i:= 1 to Paramcount do
      chambai(ParamStr(i));
    if xem then begin
      writeln('----------------');
      writeln('CHAM THI TU DONG - BY TRAN HUU NAM - thnam@thptnguyendu.edu.vn');
      readln;
    end;
  end;
end.
